import pygame.display, sys
from collections import namedtuple

grids_data = [[0] * 10] * 10
group = pygame.sprite.Group()
player = pygame.sprite.Group()
grids_positions_start = []
grids_positions_end = []
config = namedtuple("config", ["window_size", "tiled_size", 'grid_size'])(
    [300, 300],
    [100, 100],
    [10 * (100 + 10), 10 * (100 + 10)]
)
camera_center = [int(item / 2) for item in config.window_size]
real_center = [0, 0]

"""
model =  {"name":"","callable":[]}
"""
events = []


def register_event(name: str, _callable: callable):
    global events
    result = list(filter(lambda item: item['name'] == name, events))
    if len(result) > 0:
        result = result[0]
        result['callable'] += [_callable]
    else:
        events += [{"name": name, "callable": [_callable]}]


def remove_event(name: str, _callable: callable):
    global events
    result = list(filter(lambda item: item['name'] == name, events))
    if len(result) > 0:
        result = result[0]
        for __callable in result['callable']:
            if _callable == __callable:
                result['callable'].remove(__callable)
        if len(result['callable']) <= 0:
            events.remove(result)


class tiled(pygame.sprite.Sprite):
    def __init__(self, size, position):
        super().__init__()
        self.image = pygame.Surface(size)
        self.image.fill([255, 0, 0])
        self.rect = self.image.get_rect()
        self.rect.topleft = position


class target(pygame.sprite.Sprite):
    def __init__(self, position=[0, 0]):
        super().__init__()
        self.image = pygame.Surface([10, 10])
        self.image.fill([255, 255, 0])
        self.rect = self.image.get_rect()
        self.rect.center = position
        self.speed = 1

        register_event("target event", self.movement)

    def move(self, speed):
        if camera_center[0] < real_center[0] < config.grid_size[0] - camera_center[0]:
            self.rect.centerx = camera_center[0]
        else:
            self.rect.x += speed[0]
        if camera_center[1] < real_center[1] < config.grid_size[1] - camera_center[1]:
            self.rect.centery = camera_center[1]
        else:
            self.rect.y += speed[1]

    def movement(self, event):
        if pygame.key.get_pressed()[pygame.K_a]:
            self.move([-1, 0])
        if pygame.key.get_pressed()[pygame.K_d]:
            self.move([1, 0])
        if pygame.key.get_pressed()[pygame.K_w]:
            self.move([0, -1])
        if pygame.key.get_pressed()[pygame.K_s]:
            self.move([0, 1])


def movement(speed=[1, 1]):
    if 0 < real_center[0] < camera_center[0]:
        resize_grids_position(is_x=True, is_start=True)
    elif config.grid_size[0] - camera_center[0] < real_center[0] < config.grid_size[0]:
        resize_grids_position(is_x=True, is_end=True)
    else:
        for sp in group.sprites():
            sp.rect.x -= speed[0]
    if 0 < real_center[1] < camera_center[1]:
        resize_grids_position(is_y=True, is_start=True)
    elif config.grid_size[1] - camera_center[1] < real_center[1] < config.grid_size[1]:
        resize_grids_position(is_y=True, is_end=True)
    else:
        for sp in group.sprites():
            sp.rect.y -= speed[1]


def camera_event(event):
    if pygame.key.get_pressed()[pygame.K_a]:
        real_center[0] -= 1
        movement([-1, 0])
    if pygame.key.get_pressed()[pygame.K_d]:
        real_center[0] += 1
        movement([1, 0])
    if pygame.key.get_pressed()[pygame.K_w]:
        real_center[1] -= 1
        movement([0, -1])
    if pygame.key.get_pressed()[pygame.K_s]:
        real_center[1] += 1
        movement([0, 1])


def resize_grids_position(is_x=True, is_y=False, is_start=True, is_end=False):
    if is_start:
        grids = grids_positions_start
    if is_end:
        grids = grids_positions_end
    for index, tiled in enumerate(group):
        if is_x:
            tiled.rect.x = grids[index][0]
        if is_y:
            tiled.rect.y = grids[index][1]


if __name__ == '__main__':
    pygame.init()

    register_event("camera event", camera_event)
    screen = pygame.display.set_mode(config.window_size)

    for y, row in enumerate(grids_data):
        for x, col in enumerate(row):
            position = [(x * (config.tiled_size[0] + 10)), y * (config.tiled_size[1] + 10)]
            grids_positions_start += [position]
            grids_positions_end += [[-config.grid_size[0] + position[0] + config.window_size[0] + 10,
                                     -config.grid_size[1] + position[1] + config.window_size[1] + 10]]
            group.add(tiled(config.tiled_size, position))
    player.add(target())

    while True:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                sys.exit()
            for _event in events:
                [_e(event) for _e in _event['callable']]

        screen.fill((0, 0, 0))
        group.draw(screen)
        group.update()
        player.draw(screen)
        player.update()

        pygame.display.update()
